/****************************************************************************
 * Dirve lcd19264 , mode 8080. Tested on ABG192064C00-v1.0
 * Tested MCU MKE06Z
 * Owner : apleilx
 * 
 * Frequence : 40MHz
 * 
 * Some samble :
 * CN------------Chianese
 * Page----------The Page of LCD
 * Col-----------The col address of LCD
 * 16_16_________A char that is 16*16 dots
 * U-------------The chip select for LCD,(CS1--left / CS2--middle / CS3--right)
 * Com-----------LCD command
 * Disp----------Display
 * W-------------write
 * Rec-----------recently
 * Img-----------Image
 * Anti----------Anti-color
 * DIR-----------direction
 * DEL-----------delay
 * Explanation:
 * ANSEL must be set again
 * This file relay on ASCII_16_32.c,ASCII_8_16
 * that is in pic18f2xk22flash.h .
 * ASCII_16_32.c and ASCII_8_16 is unrelaied on hardware ,you can copy those to your project.
 * Imag.c and CN_16_16_Index.c is not necessary ,unless you want to display chianese and image.
 * **************************************************************************/

#include "lcd19264.h"
/**************************************************************************
 * define control bit for LCD19264
 *************************************************************************/
 
#define LCD_CS1_MASK                    (0x08000000UL)
#define LCD_CS2_MASK                    (0x00000100UL)
#define LCD_CS3_MASK                    (0x00000200UL)
#define LCD_E_MASK                      (0x04000000UL)
#define LCD_RW_MASK                     (0x00000008UL)
#define LCD_RS_MASK                     (0x00000004UL)
#define LCD_RST_MASK                    (0x10000000UL)
#define LCD_BUSY_MASK                   ((1UL << 23))
#define LCD_DATA_MASK                   (0xFFUL << 16)
#define LCD_DATA_POS                    (16)
 
#define LCD19264_CS1_Enable()           (FGPIOA->PCOR = LCD_CS1_MASK)
#define LCD19264_CS1_Disable()          (FGPIOA->PSOR = LCD_CS1_MASK) 

#define LCD19264_CS2_Enable()           (FGPIOB->PCOR = LCD_CS2_MASK)
#define LCD19264_CS2_Disable()          (FGPIOB->PSOR = LCD_CS2_MASK) 

#define LCD19264_CS3_Enable()           (FGPIOB->PCOR = LCD_CS3_MASK)
#define LCD19264_CS3_Disable()          (FGPIOB->PSOR = LCD_CS3_MASK) 

#define LCD19264_E(x)                   ((x)?(FGPIOA->PSOR = LCD_E_MASK):(FGPIOA->PCOR = LCD_E_MASK))
#define LCD19264_RW(x)                  ((x)?(FGPIOA->PSOR = LCD_RW_MASK):(FGPIOA->PCOR = LCD_RW_MASK))
#define LCD19264_RS(x)                  ((x)?(FGPIOA->PSOR = LCD_RS_MASK):(FGPIOA->PCOR = LCD_RS_MASK))
#define LCD19264_RESET(x)               ((x)?(FGPIOA->PSOR = LCD_RST_MASK):(FGPIOA->PCOR = LCD_RST_MASK))

#define LCD19264_BUSY()                 ( FGPIOB->PDIR & LCD_BUSY_MASK)     

#define LCD19264_DATA_IN()              FGPIOB->PDDR &= ~LCD_DATA_MASK              
#define LCD19264_DATA_OUT()             FGPIOB->PDDR |= LCD_DATA_MASK

/*****************************************************************************
 * DATA read and write
 ****************************************************************************/
 /*
__STATIC_INLINE uint8_t LCD19264_DATA_Read()
{	
	return (uint8_t)( (FGPIOB->PDIR >> LCD_DATA_POS) & 0xFF);
}
*/
__STATIC_INLINE void LCD19264_DATA_Write(uint8_t Data)
{
	FGPIOB->PCOR = LCD_DATA_MASK;
	FGPIOB->PSOR = (Data << LCD_DATA_POS);
}	

/*****************************************************************************
 *lcd E delay , 450ns
 ****************************************************************************/
__STATIC_INLINE void LCD19264_E_DEL()
{
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
}

__STATIC_INLINE void LCD19264_Com_DEL()
{
	__nop();__nop();__nop();__nop();__nop();
}

__STATIC_INLINE void LCD19264_Sub_DEL()
{
	__nop();
}

/*****************************************************************************
 *lcd Ready delay , 350ns 
 ****************************************************************************/
__STATIC_INLINE void LCD19264_Read_DEL()
{
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
}

__STATIC_INLINE void LCD19264_RESET_DEL()
{
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
}

static unsigned char LCDDataMask;
static unsigned char WaitTimeOutCnt;
static FontType FontCur;
/****************************************************************************
 * The flowing function can be run independently.
 * Other functions rely on these functions.
 * so if you delete it, some functions may not work.
 ****************************************************************************/

void LCD19264_Busy(void);
void LCD19264_DataW(unsigned char Data);
void LCD19264_ComW(unsigned char Command);
void LCD19264_WriteCom_CS1(unsigned char Content);
void LCD19264_WriteCom_CS2(unsigned char Content);
void LCD19264_WriteCom_CS3(unsigned char Content);
void LCD19264_WriteCom(unsigned char U,unsigned char Content);

/******************************************************************************
 * Init
 * @param Content
 *****************************************************************************/
void LCD19264Init()
{
	 FGPIOA->PDDR |= LCD_CS1_MASK;
	 FGPIOB->PDDR |= LCD_CS2_MASK;
	 FGPIOB->PDDR |= LCD_CS3_MASK;
		
	 LCD19264_CS1_Disable();
	 LCD19264_CS2_Disable();
	 LCD19264_CS3_Disable();

	 FGPIOA->PDDR |= LCD_E_MASK;
	 LCD19264_E(0);
	 FGPIOA->PDDR |= LCD_RW_MASK;
	 LCD19264_RW(0);
	 FGPIOA->PDDR |= LCD_RS_MASK;
	 LCD19264_RS(0);

	 FGPIOB->PIDR &= ~LCD_DATA_MASK;
	 LCD19264_DATA_OUT();

	 FGPIOA->PDDR |= LCD_RST_MASK;
	 LCD19264_RESET(0);
	 LCD19264_RESET_DEL();
	 LCD19264_RESET(1);
	 LCD19264_RESET_DEL();
	 LCD19264_RESET(0);
	 LCD19264_RESET_DEL();
	 LCD19264_RESET(1);
	 //0x3E - display OFF
	 LCD19264_WriteCom_CS1(0x3E);  
	 LCD19264_WriteCom_CS2(0x3E);
	 LCD19264_WriteCom_CS3(0x3E);
	 //0x3F - display ON 
	 LCD19264_WriteCom_CS1(0x3F);   
	 LCD19264_WriteCom_CS2(0x3F);
	 LCD19264_WriteCom_CS3(0x3F);
	 //set start line
	 LCD19264_WriteCom_CS1(0xc0);  
	 LCD19264_WriteCom_CS2(0xc0);
	 LCD19264_WriteCom_CS3(0xc0);
	 
	 LCDDataMask = 0x00;
	 WaitTimeOutCnt = 0;
	 
	 LCD19264ScreenFill(0x00);
}

/******************************************************************************
 * Lcd Font Set
 * @param Content
 *****************************************************************************/
void LcdFontSet(FontType Font)
{
	FontCur = Font;
}

/******************************************************************************
 * Waiting LCD untile it is not busy
 * @param Content
 *****************************************************************************/
void LCD19264_Busy()
{
	uint16_t TimeOutSt;
	
    LCD19264_RS(0);           //read status reg
    LCD19264_RW(1);
    LCD19264_Com_DEL();
    LCD19264_E(1);
	LCD19264_DATA_IN();
    LCD19264_Read_DEL();
	
#if LCD19264_ENABLE > 0 
	if(WaitTimeOutCnt < LCD_WAIT_TIME_OUT_LIMIT)
	{
		TimeOutSt = 0;
		while(LCD19264_BUSY() != 0)
		{
			if((TimeOutSt ++) > LCD_WAIT_TIME_OUT_CYCLE_CNT)
				break;
		}
		
		if(TimeOutSt > LCD_WAIT_TIME_OUT_CYCLE_CNT)
			WaitTimeOutCnt ++;
		else
			WaitTimeOutCnt = 0;
	}
#endif
	
    LCD19264_E(0);
}

/******************************************************************************
 * Write command to lcd
 * @param Content
 *****************************************************************************/
void LCD19264_ComW(unsigned char Command)
{
    LCD19264_Busy();
    LCD19264_RS(0);         //com
    LCD19264_RW(0);        //write
    LCD19264_DATA_OUT();
    LCD19264_DATA_Write(Command);
    LCD19264_Com_DEL();
    LCD19264_E(1);
    LCD19264_E_DEL();
    LCD19264_E(0);
}

/******************************************************************************
 * Write data to lcd
 * @param Content
 *****************************************************************************/
void LCD19264_DataW(unsigned char Data)
{
    LCD19264_Busy();
    LCD19264_RS(1);        //data
    LCD19264_RW(0);        //write
    LCD19264_DATA_OUT();
    LCD19264_DATA_Write(Data);
    LCD19264_Com_DEL();
    LCD19264_E(1);
    LCD19264_E_DEL();
    LCD19264_E(0);
}

/******************************************************************************
 * Write command to CS1
 * @param Content
 *****************************************************************************/
void LCD19264_WriteCom_CS1(unsigned char Content)
{
    LCD19264_CS1_Enable();
    LCD19264_CS2_Disable();
    LCD19264_CS3_Disable();

    LCD19264_ComW(Content);
}

/******************************************************************************
 * Write command to CS2
 * @param Content
 *****************************************************************************/
void LCD19264_WriteCom_CS2(unsigned char Content)
{
    LCD19264_CS1_Disable();
    LCD19264_CS2_Enable();
    LCD19264_CS3_Disable();
	
	LCD19264_ComW(Content);
}

/******************************************************************************
 * Write command to CS3
 * @param Content
 *****************************************************************************/
void LCD19264_WriteCom_CS3(unsigned char Content)
{
    LCD19264_CS1_Disable();
    LCD19264_CS2_Disable();
    LCD19264_CS3_Enable();
	
    LCD19264_ComW(Content);
}

/******************************************************************************
 * Write command to CS1/CS2/CS3
 * @param Content
 *****************************************************************************/
void LCD19264_WriteCom(unsigned char U,unsigned char Content)
{
    if(U == 1)
    {
		LCD19264_WriteCom_CS1(Content);
    }
    else if(U == 2)
    {
		LCD19264_WriteCom_CS2(Content);

    }
    else
    {
		LCD19264_WriteCom_CS3(Content);
    }
}


/******************************************************************************
 * Clear the screen
 * @param Content
 *****************************************************************************/
void LCD19264ScreenFill(unsigned char Data)
{
    unsigned char Col,Page;
	unsigned char LMR;

	for(LMR = 1; LMR<4; LMR++)
	{	
		for(Page=0;Page<8;Page++)
		{
			LCD19264_WriteCom(LMR,0xB8+Page);
			LCD19264_WriteCom(LMR,0x40);
			for(Col=0;Col<64;Col++)
			{
				LCD19264_Sub_DEL();
				LCD19264_DataW(Data);
			}
		}
	}
    __nop();
}


/******************************************************************************
 * Set start line on screen
 * @param Content
 *****************************************************************************/
void LCD19264StartLineAll(unsigned char Line)
{
    LCD19264_WriteCom_CS1( 0xC0 + Line);
    LCD19264_WriteCom_CS2( 0xC0 + Line);
    LCD19264_WriteCom_CS3( 0xC0 + Line);
}

/******************************************************************************
 * Set start line on screen
 * @param Content
 *****************************************************************************/
void LCD19264StartLine(unsigned char U,unsigned char Line)
{
    switch(U)
    {
        case 1:
			LCD19264_WriteCom_CS1( 0xC0 + Line);
			break;
        case 2:
			LCD19264_WriteCom_CS2( 0xC0 + Line);
			break;
        case 3:
			LCD19264_WriteCom_CS3( 0xC0 + Line);
			break;
        default:
			break;
    }
}

void LCD19264DispAnti()
{
	LCDDataMask = 0xFF;
}

void LCD19264DispNormal()
{
	LCDDataMask = 0x00;
}

void LCD19264PutCh(unsigned char Page,unsigned char Col,unsigned char DispCh)
{
    unsigned char LMR;              // left /middle /right screen
    unsigned char ColCounter;
	
    ColCounter = 0;
    if(Page >= 7) return;
    if(Col > 184) return;
    LMR = 1;
    while(Col >= 64)
    {
        Col -= 64;
        LMR ++;
    }
    if(Col > 56) return;
	
    LCD19264_WriteCom(LMR,0xB8 + Page);           // Set the first Page of the char
    LCD19264_WriteCom(LMR,0x40 + Col);            // Set the start Col of the char
    while(ColCounter < 8)                         //Write the first page of the char
    {
       LCD19264_DataW( ASCII_SongTi_8_16[DispCh - 0x20][ColCounter ++] ^ LCDDataMask );
    }
	
    LCD19264_WriteCom(LMR,0xB8 + Page + 1);       // Set the second Page of the char
    LCD19264_WriteCom(LMR,0x40 + Col);            // Set start Col of the char
    while(ColCounter < 16)                        //Write the first page of the char
    {
       LCD19264_DataW( ASCII_SongTi_8_16[DispCh - 0x20][ColCounter ++] ^ LCDDataMask );
    }
}
/******************************************************************************
 * This function only can display ASCII character , if you send a character
 * that is not between ' 'and '~'in ASCII ,the function will be stop and out;
 *
 * The Page must between 0 and 4
 * The Col must between 0 and 191,and is a multiple of 16.
 * @param Content
 *****************************************************************************/
void LCD19264PutChBig(unsigned char Page,unsigned char Col,unsigned char DispCh)
{
    unsigned char LMR;              // left /middle /right screen
    unsigned char ColCounter;
    unsigned char Index;
	unsigned char j;
	
    ColCounter = 0;
    Index = 0;
    if(Page > 4) return;
    if(Col > 176) return;
    LMR = 1;
    while(Col >= 64)
    {
        Col -= 64;
        LMR ++;
    }
    if(Col > 48) return;
	
    for(j=0;j<4;j++)
    {
        ColCounter = 0;
        LCD19264_WriteCom(LMR,0xB8 + Page +j);           // Set the first Page of the char
        LCD19264_WriteCom(LMR,0x40 + Col);               // Set the start Col of the char
        while(ColCounter < 16)                           //Write the first page of the char
        {
			LCD19264_DataW( ASCII_SongTi_16_32[DispCh - 0x20][Index ++] ^ LCDDataMask );
			ColCounter ++;
        }
    }
}

/******************************************************************************
 * This function used to display a string ,the string must be end of '\n'.
 * If it is not end of '\n' ,you may be see something unknown on your lcd.
 * This function only can display ASCII character , if you send a character
 * that is not between ' 'and '~'in ASCII ,the function will be stop and out;
 *
 * The Page must between 0 and 6
 * The Col must between 0 and 191,and is a multiple of 8.
 *
 * Fnally,this is just a simple function that does not support formatted output.
 * @param Content
 *****************************************************************************/
void LCD19264Puts(unsigned char Page,unsigned char Col,unsigned char *DispStr)
{
    unsigned char LMR;              // left /middle /right screen
    unsigned char DispCh = 0;
    unsigned char ColCounter;
    unsigned char ColTest;
	
    ColTest = Col;
    if(Page >= 8) return;
    if(Col >= 192) return;
    while(ColTest >= 8)
    {
        ColTest -= 8;
    }
    if(ColTest != 0) return;

    LMR = 1;
    while(Col >= 64)
    {
        Col -= 64;
        LMR ++;
    }

    ColCounter = 0;
    while(LMR < 4)
    {
        while(Col <= 56)
        {
            LCD19264_WriteCom(LMR,0xB8 + Page);           // Set the first Page of the char
            LCD19264_WriteCom(LMR,0x40 + Col);            // Set the start Col of the char

            DispCh = *(DispStr ++);                       //Read a char to display
            if( (DispCh >126) || (DispCh <32) ) return;   //test the char leagle or not

            while(ColCounter < 8)                         //Write the first page of the char
            {
                LCD19264_DataW( ASCII_SongTi_8_16[DispCh - 0x20][ColCounter ++] ^ LCDDataMask );
            }
            LCD19264_WriteCom(LMR,0xB8 + Page + 1);       // Set the second Page of the char
            LCD19264_WriteCom(LMR,0x40 + Col);            // Set start Col of the char
            while(ColCounter < 16)                        //Write the first page of the char
            {
                LCD19264_DataW( ASCII_SongTi_8_16[DispCh - 0x20][ColCounter ++] ^ LCDDataMask );
            }
            Col += 8;                                      //after write a char,Col point the next place
            ColCounter = 0;
        }
        LMR ++ ;
        Col = 0;
    }
}

/******************************************************************************
 * This function used to display a chinese ,t
 * The Page must between 0 and 6
 * The Col must between 0 and 176,and is a multiple of 16.
 *
 * Fnally,this is just a simple function that does not support formatted output.
 * @param Content
 *****************************************************************************/
void LCD19264PutCN(unsigned char Page,unsigned char Col,const unsigned char *pCN)
{
    unsigned char LMR;              // left /middle /right screen
    unsigned char ColCounter;
    ColCounter = 0;
    if(Page > 6) return;
    if(Col > 176) return;
    LMR = 1;
    while(Col >= 64)
    {
        Col -= 64;
        LMR ++;
    }
    if(Col > 48) return;
    //_FlashReadBytes((unsigned long )(pCN) ,32,LCD19264_DispBuffer );
    LCD19264_WriteCom(LMR,0xB8 + Page);           // Set the first Page of the char
    LCD19264_WriteCom(LMR,0x40 + Col);            // Set the start Col of the char
    while(ColCounter < 16)                         //Write the first page of the char
    {
		LCD19264_DataW( pCN[ColCounter ++] ^ LCDDataMask );
    }
    LCD19264_WriteCom(LMR,0xB8 + Page + 1);       // Set the second Page of the char
    LCD19264_WriteCom(LMR,0x40 + Col);            // Set start Col of the char
    while(ColCounter < 32)                        //Write the first page of the char
    {
		LCD19264_DataW( pCN[ColCounter ++] ^ LCDDataMask );
    }
}

/******************************************************************************
 * This function used to display image in one line
 *
 * @param Content
 *****************************************************************************/
void LCD19264LineDisp(unsigned char Page ,unsigned char Col,unsigned char Wide,const unsigned char *pImg)
{
    unsigned char StartLMR ,RecLMR;
    unsigned char ColRel;
    unsigned char WideUnprinted;
	unsigned char Index;

    StartLMR = 1;
    ColRel = Col;
    while(ColRel > 63)
    {
        ColRel -= 64;
        StartLMR ++;
    }

    RecLMR = StartLMR;
    WideUnprinted = Wide;
	Index = 0;
    /************************************Wriet Data****************************/
	while(RecLMR < 4)                                //Screen is not over
	{
		LCD19264_WriteCom(RecLMR,0xB8 + Page);       //Set Page
		LCD19264_WriteCom(RecLMR,0x40 + ColRel);     //Set Start Col

		while((ColRel++ < 64))
		{
			LCD19264_DataW(pImg[Index ++] ^ LCDDataMask );
			if((--WideUnprinted) == 0 ) return;
		}
		RecLMR ++;
		ColRel = 0;
	}
    /*************************************************************************/
}

/****************************************************************************
 * The High must be a multiple of 8.
 ****************************************************************************/
void LCD19264ImgDisp(unsigned char StartPage ,unsigned char StartCol ,\
unsigned char Wide ,unsigned char PageHigh ,const unsigned char *pImg)
{
    while( (PageHigh > 0 ) && (StartPage < 8) )
    {
        LCD19264LineDisp(StartPage,StartCol,Wide,pImg);
        pImg += Wide;
        PageHigh --;
        StartPage ++;
    }
}

